package pub.gll.onepeas.libbase.track.manager

import android.R
import android.app.Activity
import android.app.Application
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import pub.gll.onepeas.libbase.track.annotation.ViewExposure
import pub.gll.onepeas.libbase.track.api.IViewExposure
import pub.gll.onepeas.libbase.track.util.ViewExposureLog
import pub.gll.onepeas.libbase.track.constants.ViewExposureConstants
import pub.gll.onepeas.libbase.track.ui.KKTrackerExposureLayout
import java.util.*

/**
 * <pre>
 *     @author : whg
 *     @e-mail : guanhuawei@kkworld.com
 *     @time   : 2021/6/17 15:12
 *     @desc   : view曝光管理类，进行曝光的初始化
 *     @version: 1.0
 * </pre>
 */
class KKVEManager private constructor() {

    private var mCurrentExposureLayout: KKTrackerExposureLayout? = null
    private var mLastExposureLayout: KKTrackerExposureLayout? = null
    private var mActivityLifecycle: ActivityLifecycleForViewExposure? = null
    private var openViewExposure: Boolean = true


    /**
     * 内部初始化,注册声明周期
     */
    fun init(application: Application?, openEV: Boolean, logOpen: Boolean) {
        openViewExposure = openEV
        application?.let {
            if (openViewExposure) {
                ViewExposureLog.logOpen(logOpen)
                mActivityLifecycle = ActivityLifecycleForViewExposure()
                it.registerActivityLifecycleCallbacks(mActivityLifecycle)
            }
        }
    }

    /**
     * 开启和关闭view曝光
     * @param openEV Boolean
     */
    fun openViewExposure(openEV: Boolean) {
        openViewExposure = openEV
        if (!openViewExposure) {
            // 如果关闭曝光，需要清除当前缓存的页面
            mLastExposureLayout = null
            mCurrentExposureLayout = null
        }
    }


    /**
     * unregister Activity Lifecycle Callbacks
     *
     * @param application
     */
    fun unInit(application: Application?) {
        if (mActivityLifecycle != null) {
            application?.unregisterActivityLifecycleCallbacks(mActivityLifecycle)
        }
    }

    /**
     * view绑定id和公共事件
     * @param view View?  需要绑定的view
     * @param dataMap HashMap<String, Any>? 公共事件map
     */
    fun veBindData(
        view: View?,
        data: Any?,
        dimThreshold: Float = 0.7f,
        dataMap: HashMap<String, Any>? = null
    ) {
        veBindData(view, data, dimThreshold, dataMap, null)
    }

    /**
     * view绑定id和设置曝光的监听
     * @param view View? 需要绑定的view
     * @param listener IViewExposure? 曝光回调的listener
     */
    fun veBindData(
        view: View?,
        data: Any?,
        dimThreshold: Float = 0.7f,
        listener: IViewExposure? = null
    ) {
        veBindData(view, data, dimThreshold, null, listener)
    }

    /**
     * 解绑注册过的view
     */
    fun unVeBindData(view: View?) {
        if (view == null) return
        mCurrentExposureLayout?.let {
            KKExposurePageManager.getExposureManager(it)?.removeBindView(it, view)
        }
    }

    /**
     * view绑定id、公共事件和设置曝光的回调
     * @param view View?
     * @param dataMap HashMap<String, Any>?
     * @param listener IViewExposure?
     */
    fun veBindData(
        view: View?,
        data: Any?,
        dimThreshold: Float = 0.7f,
        dataMap: HashMap<String, Any>? = null,
        listener: IViewExposure? = null
    ) {
        if (view == null) {
//            ErrorReporter.get()
//                .reportAndThrowIfDebug(IllegalArgumentException("view  should not null"))
            return
        }
        if (data == null) {
//            ErrorReporter.get()
//                .reportAndThrowIfDebug(IllegalArgumentException("data  should not null"))
            return
        }

        checkBindStatue(view, data)

        view.setTag(ViewExposureConstants.VIEW_TAG_UNIQUE_NAME, data)
        view.setTag(ViewExposureConstants.VIEW_TAG_DIM_THRESHOLD, dimThreshold)
        view.setTag(ViewExposureConstants.VIEW_TAG_PARAM, dataMap)
        view.setTag(ViewExposureConstants.VIEW_TAG_LISTENER, listener)
        //绑定时加入列表
        mCurrentExposureLayout?.let {
            KKExposurePageManager.getExposureManager(it)?.addBindView(it, view)
        }
    }

    /**
     * 绑定之前校验是否之前绑定过。
     * 解决问题：同一个view换绑不同的tag，但是当前view并没有时机的变化都是可见的，如果不单独处理就缺少一次不可见的时机
     * @param view View
     * @param data Any
     */
    private fun checkBindStatue(view: View, data: Any) {
        mCurrentExposureLayout?.let {
            KKExposurePageManager.getExposureManager(it)?.checkBindStatus(it, view, data)
        }
    }


    /**
     * 监听页面的生命周期，用于获取每个页面的view tree
     */
    private inner class ActivityLifecycleForViewExposure : Application.ActivityLifecycleCallbacks {

        override fun onActivityCreated(activity: Activity, bundle: Bundle?) {

        }

        override fun onActivityStarted(activity: Activity) {}

        /**
         * onresume attach
         * @param activity Activity
         */
        override fun onActivityResumed(activity: Activity) {
            if (openViewExposure) {
                onAttachActivityResumed(activity)
            }
        }

        override fun onActivityPaused(activity: Activity) {
            if (openViewExposure) {
                onAttachActivityPaused()
            }
        }

        override fun onActivityStopped(activity: Activity) {}

        /**
         * ondestory detach
         * @param activity Activity
         */
        override fun onActivityDestroyed(activity: Activity) {
            if (openViewExposure) {
                detachExposureFrameLayout(activity)
            }
        }

        override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
    }

    private fun onAttachActivityPaused() {
        if (mCurrentExposureLayout != null) {
            mLastExposureLayout = mCurrentExposureLayout
        }
    }


    /**
     * 获取运行时注解，获取策略
     * @param activity 当前页面
     */
    private fun onAttachActivityResumed(activity: Activity?) {
        try {
            initCurrentExposureLayout();
            // 获取注解如果没有注解，直接返回
            getExposureAnnotation(activity) ?: return
            // wrapConentView
            mCurrentExposureLayout = attachExposureFrameLayout(activity) as? KKTrackerExposureLayout?
        } catch (e: Exception) {
        }
    }

    private fun initCurrentExposureLayout() {
        mCurrentExposureLayout = null;
    }

    /**
     * 判断当前页面是否曝光
     */
    private fun getExposureAnnotation(activity: Activity?): ViewExposure? {
        if (activity == null) return null
        return activity.javaClass?.getAnnotation(ViewExposure::class.java)
    }


    /**
     * attach to activity，become contenview's parent view
     * @param activity Activity?
     */
    private fun attachExposureFrameLayout(activity: Activity?): ViewGroup? {
        val container = activity?.findViewById<View>(R.id.content) as? ViewGroup ?: return null
        if (container is KKTrackerExposureLayout) {
            ViewExposureLog.d("attachExposureFrameLayout container is KKTrackerExposureLayout ")
            return container
        }
        return getKkViewExposureLayout(container, activity)
    }

    /**
     * 使KKTrackerExposureLayout成为decorview的直接子view
     */
    fun getKkViewExposureLayout(container: ViewGroup, activity: Activity?): ViewGroup? {
        if (activity == null) return null
        var kkTrackerExposureLayout: KKTrackerExposureLayout? = null
        try {
            //DecorView
            if (container.childCount > 0) {
                val root = container.getChildAt(0)
                if (root is KKTrackerExposureLayout) {
                    kkTrackerExposureLayout = root
                    ViewExposureLog.d(" root is KKTrackerExposureLayout ")
                } else {
                    //层级在contentView之上
                    val exposureLayout = KKTrackerExposureLayout(activity)
                    while (container.childCount > 0) {
                        val view = container.getChildAt(0)
                        container.removeViewAt(0)
                        exposureLayout?.addView(view, view.layoutParams)
                    }

                    container.addView(
                        exposureLayout,
                        ViewGroup.LayoutParams(
                            ViewGroup.LayoutParams.MATCH_PARENT,
                            ViewGroup.LayoutParams.MATCH_PARENT
                        )
                    )
                    kkTrackerExposureLayout = exposureLayout
                }
            }
        } catch (e: Exception) {
            ViewExposureLog.e("attachExposureFrameLayout ${e.message} ")
        }
        return kkTrackerExposureLayout
    }

    /**
     * detach from activity
     */
    private fun detachExposureFrameLayout(activity: Activity?) {
        if (activity == null) {
            return
        }

        try {
            getExposureAnnotation(activity) ?: return

            var container = activity.findViewById<View>(R.id.content) as? ViewGroup ?: return

            if (container is KKTrackerExposureLayout) {
                ViewExposureLog.d("detachExposureFrameLayout container is KKTrackerExposureLayout ")
                var parent = container.parent
                ViewExposureLog.d("parent = ${parent} ")
                mLastExposureLayout?.let {
                    KKExposurePageManager.getExposureManager(it)?.clearBindViews(it)
                    ViewExposureLog.e("detachExposureFrameLayout mLastExposureLayout clear all bindviews ")
                    mLastExposureLayout = null
                }
            } else if (container.getChildAt(0) is KKTrackerExposureLayout) {
                ViewExposureLog.d("detachExposureFrameLayout container's first child is KKTrackerExposureLayout ")
                mLastExposureLayout?.let {
                    KKExposurePageManager.getExposureManager(it)?.clearBindViews(it)
                    container.post(Runnable {
                        container.removeViewAt(0)
                    })
                    mLastExposureLayout = null
                    ViewExposureLog.e("detachExposureFrameLayout mLastExposureLayout clear all bindviews ")
                }
            }
        } catch (e: Exception) {
            ViewExposureLog.e("detachExposureFrameLayout ${e.message} ")
        }
    }


    companion object {
        private var sInstance: KKVEManager? = null

        /**
         * 单例对象
         */
        @JvmStatic
        fun getInstance(): KKVEManager {
            if (sInstance == null) {
                synchronized(KKVEManager::class.java) {
                    if (sInstance == null) {
                        sInstance = KKVEManager()
                    }
                }
            }
            return sInstance!!
        }
    }

}